/*
 *  Copyright 2011 Alexey Andreev.
 * 
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 * 
 *       http://www.apache.org/licenses/LICENSE-2.0
 * 
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *  under the License.
 */
package org.xthl.core;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.nop.core.BeanScope;
import org.nop.core.ManagedBean;
import org.nop.sql.DataManager;
import org.nop.sql.DataResult;
import org.nop.sql.QueryBuilder;
import org.nop.util.Injected;
import org.xthl.core.api.Fragment;
import org.xthl.core.api.FragmentLogic;
import org.xthl.core.api.FragmentType;
import org.xthl.core.data.FragmentSource;
import org.xthl.core.data.FragmentTypeSource;

/**
 *
 * @author Alexey Andreev
 */
@ManagedBean(iface = FragmentLogic.class, scope = BeanScope.REQUEST)
public class FragmentLogicImpl implements FragmentLogic {
    private DataManager dataManager;
    private FragmentsFeature feature;
    private QueryBuilder qb;
    
    @Injected
    public FragmentLogicImpl(DataManager dataManager, FragmentsFeature feature) {
        this.dataManager = dataManager;
        this.feature = feature;
        this.qb = dataManager.getQueryBuilder();
    }

    @Override
    public void enableFragments(Set<Integer> fragmentIds, boolean enable) {
        FragmentSource fragment = qb.get(FragmentSource.class);
        List<Integer> idList = new ArrayList<Integer>(fragmentIds);
        Collections.sort(idList);
        int batch = 100;
        for (int i = 0; i < idList.size(); i += batch) {
            int top = Math.min(idList.size(), i + batch);
            dataManager.exec(qb.with(fragment)
                    .filter(fragment.id().inNumbers(idList.subList(i, top)))
                    .set(fragment.enabled(), enable));
        }
    }
    
    private Fragment mapFragment(DataResult result) {
        Fragment fragment = new Fragment();
        fragment.setId(result.getInt(1));
        fragment.setImplementor(feature.getFragment(result.getInt(1)));
        fragment.setPosition(result.getInt(3));
        fragment.setEnabled(result.getBoolean(4));
        FragmentType type = new FragmentType();
        type.setId(result.getInt(5));
        type.setJavaType(feature.getFragmentType(result.getInt(5)));
        fragment.setType(type);
        return fragment;
    }

    @Override
    public List<Fragment> getFragments(int fragmentTypeId) {
        FragmentSource fragment = qb.get(FragmentSource.class);
        FragmentTypeSource type = qb.get(FragmentTypeSource.class);
        DataResult result = dataManager.exec(qb.with(fragment)
                .join(type, type.id().eq(fragment.typeId()))
                .filter(fragment.typeId().eq(fragmentTypeId))
                .sortAsc(fragment.position())
                .fetch(fragment.id(), fragment.name(), fragment.position(),
                        fragment.enabled(), type.id(), type.name()));
        List<Fragment> dtos = new ArrayList<Fragment>();
        while (result.next()) {
            dtos.add(mapFragment(result));
        }
        return dtos;
    }

    @Override
    public Fragment getFragment(int fragmentId) {
        FragmentSource fragment = qb.get(FragmentSource.class);
        FragmentTypeSource type = qb.get(FragmentTypeSource.class);
        DataResult result = dataManager.exec(qb.with(fragment)
                .join(type, type.id().eq(fragment.typeId()))
                .filter(fragment.id().eq(fragmentId))
                .fetch(fragment.id(), fragment.name(), fragment.position(),
                        fragment.enabled(), type.id(), type.name()));
        return result.next() ? mapFragment(result) : null;
    }

    @Override
    public List<Fragment> getEnabledFragments(int fragmentTypeId) {
        FragmentSource fragment = qb.get(FragmentSource.class);
        FragmentTypeSource type = qb.get(FragmentTypeSource.class);
        DataResult result = dataManager.exec(qb.with(fragment)
                .join(type, type.id().eq(fragment.typeId()))
                .filter(fragment.typeId().eq(fragmentTypeId))
                .filter(fragment.enabled().isTrue())
                .sortAsc(fragment.position())
                .fetch(fragment.id(), fragment.name(), fragment.position(),
                        fragment.enabled(), type.id(), type.name()));
        List<Fragment> dtos = new ArrayList<Fragment>();
        while (result.next()) {
            dtos.add(mapFragment(result));
        }
        return dtos;
    }

    @Override
    public List<Fragment> getEnabledFragmentsByType(Class<? extends FragmentRenderer> cls) {
        FragmentSource fragment = qb.get(FragmentSource.class);
        FragmentTypeSource type = qb.get(FragmentTypeSource.class);
        DataResult result = dataManager.exec(qb.with(fragment)
                .join(type, type.id().eq(fragment.typeId()))
                .filter(type.name().eq(cls.getName()))
                .filter(fragment.enabled().isTrue())
                .sortAsc(fragment.position())
                .fetch(fragment.id(), fragment.name(), fragment.position(),
                        fragment.enabled(), type.id(), type.name()));
        List<Fragment> dtos = new ArrayList<Fragment>();
        while (result.next()) {
            dtos.add(mapFragment(result));
        }
        return dtos;
    }

    @Override
    public void moveFragment(int fragmentId, int targetPosition) {
        FragmentSource fragment = qb.get(FragmentSource.class);
        DataResult result = dataManager.exec(qb.with(fragment)
                .filter(fragment.id().eq(fragmentId))
                .fetch(fragment.position()));
        result.next();
        int currentPosition = result.getInt(1);
        if (currentPosition > targetPosition) {
            dataManager.exec(qb.with(fragment)
                    .filter(fragment.position().ge(targetPosition))
                    .filter(fragment.position().le(currentPosition))
                    .set(fragment.position(), qb.cond()
                        .when(fragment.position().ne(currentPosition),
                        fragment.position().add(qb.wrap(1)))
                        .otherwise(qb.wrap(targetPosition))));
        } else if (currentPosition < targetPosition) {
            dataManager.exec(qb.with(fragment)
                    .filter(fragment.position().ge(currentPosition))
                    .filter(fragment.position().le(targetPosition))
                    .set(fragment.position(), qb.cond()
                        .when(fragment.position().ne(currentPosition),
                        fragment.position().sub(qb.wrap(1)))
                        .otherwise(qb.wrap(targetPosition))));
        }
    }

    @Override
    public List<FragmentType> getFragmentTypes() {
        FragmentTypeSource type = qb.get(FragmentTypeSource.class);
        DataResult result = dataManager.exec(qb.with(type)
                .sortAsc(type.name())
                .fetch(type.id(), type.name()));
        List<FragmentType> dtos = new ArrayList<FragmentType>();
        while (result.next()) {
            FragmentType dto = new FragmentType();
            dto.setId(result.getInt(1));
            dto.setJavaType(feature.getFragmentType(result.getInt(1)));
            dtos.add(dto);
        }
        return dtos;
    }

    @Override
    public FragmentType getFragmentType(int fragmentTypeId) {
        FragmentTypeSource type = qb.get(FragmentTypeSource.class);
        DataResult result = dataManager.exec(qb.with(type)
                .filter(type.id().eq(fragmentTypeId))
                .fetch(type.id(), type.name()));
        if (!result.next()) {
            return null;
        }
        FragmentType dto = new FragmentType();
        dto.setId(result.getInt(1));
        dto.setJavaType(feature.getFragmentType(result.getInt(1)));
        return dto;
    }
}
